package cn.jbt.api.admin.service.impl;

import cn.jbt.api.admin.entity.*;
import cn.jbt.api.admin.entity.dto.ModifyPassDTO;
import cn.jbt.api.admin.entity.dto.ModifyUserDTO;
import cn.jbt.api.admin.entity.dto.ResetPassDTO;
import cn.jbt.api.admin.filter.LoginInfoAccessContext;
import cn.jbt.api.admin.repository.SysDeptRepository;
import cn.jbt.api.admin.repository.SysUserPostRepository;
import cn.jbt.api.admin.repository.SysUserRepository;
import cn.jbt.api.admin.repository.SysUserRoleRepository;
import cn.jbt.api.admin.service.ISysDeptService;
import cn.jbt.api.admin.service.ISysUserService;
import cn.jbt.common.PageQryParam;
import cn.jbt.common.PageQryResult;
import cn.jbt.common.util.RegexpUtil;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class SysUserServiceImpl implements ISysUserService {

    private final JPAQueryFactory jpaQueryFactory;
    private final SysUserRepository sysUserRepository;
    private final SysUserPostRepository sysUserPostRepository;
    private final SysUserRoleRepository sysUserRoleRepository;
    private final ISysDeptService iSysDeptService;
    private final SysDeptRepository sysDeptRepository;

    @Override
    public PageQryResult<SysUser> getAllByPage(PageQryParam<SysUser> pageQryParam) {
        SysUser qParam = pageQryParam.getParam();
        QSysUser qSysUser = QSysUser.sysUser;
        QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;
        QSysUserPost qSysUserPost = QSysUserPost.sysUserPost;

        Predicate predicate = qSysUser.isNotNull().or(qSysUser.isNull());
        if (null != qParam) {
            if (!StringUtils.isEmpty(qParam.getUsername())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.username.contains(qParam.getUsername()));
            }
            if (!StringUtils.isEmpty(qParam.getMobile())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.mobile.contains(qParam.getMobile()));
            }
            if (!StringUtils.isEmpty(qParam.getNickname())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.nickname.contains(qParam.getNickname()));
            }
            if (null != qParam.getStatus()) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.status.eq(qParam.getStatus()));
            }
            if (qParam.getDeptId() > 0) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.deptId.eq(qParam.getDeptId()));
            }
        }

        List<SysUser> list = jpaQueryFactory.select(qSysUser)
                .from(qSysUser)
                .where(predicate).orderBy(qSysUser.id.desc())
                .offset((pageQryParam.getPage() - 1) * pageQryParam.getSize())
                .limit(pageQryParam.getSize())
                .fetch()
                .stream()
                .peek(sysUser -> {
                    sysUser.setDeptName(iSysDeptService.getCascadeNamesById(sysUser.getDeptId(), "/"));

                    sysUser.setRoleIds(jpaQueryFactory.select(qSysUserRole.roleId).from(qSysUserRole)
                            .where(qSysUserRole.userId.eq(sysUser.getId())).fetch());

                    sysUser.setPostIds(jpaQueryFactory.select(qSysUserPost.postId).from(qSysUserPost)
                            .where(qSysUserPost.userId.eq(sysUser.getId())).fetch());

                    Optional.ofNullable(sysDeptRepository.findFirstById(sysUser.getDeptId()))
                            .ifPresent(sysDept -> sysUser.setDeptIds(Arrays.stream(sysDept.getNode().split("/")).filter(StringUtils::isNotEmpty)
                                    .map(Long::parseLong).collect(Collectors.toList())));
                })
                .collect(Collectors.toList());

        long total = Optional.ofNullable(jpaQueryFactory.select(qSysUser.id.count())
                .from(qSysUser)
                .where(predicate)
                .fetchOne()
        ).orElse(0L);

        return new PageQryResult<>(list, total);
    }

    @Override
    public List<SysUser> getAll(SysUser sysUser) {
        QSysUser qSysUser = QSysUser.sysUser;

        Predicate predicate = qSysUser.isNotNull().or(qSysUser.isNull());
        if (null != sysUser) {
            if (!StringUtils.isEmpty(sysUser.getUsername())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.username.contains(sysUser.getUsername()));
            }
            if (!StringUtils.isEmpty(sysUser.getMobile())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.mobile.contains(sysUser.getMobile()));
            }
            if (!StringUtils.isEmpty(sysUser.getNickname())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.nickname.contains(sysUser.getNickname()));
            }
            if (null != sysUser.getStatus()) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.status.eq(sysUser.getStatus()));
            }
            if (sysUser.getDeptId() > 0) {
                predicate = ExpressionUtils.and(predicate,
                        qSysUser.deptId.eq(sysUser.getDeptId()));
            }
        }

        return jpaQueryFactory.select(qSysUser)
                .from(qSysUser)
                .where(predicate)
                .orderBy(qSysUser.id.desc())
                .fetch();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUser add(SysUser sysUser) {
        if (RegexpUtil.isNotPassword(sysUser.getPassword())) {
            throw new RuntimeException("密码必须8-16位，并且包含字母、数字、特殊字符");
        }

        if (null != sysUserRepository.findFirstByUsername(sysUser.getUsername())) {
            throw new RuntimeException("登录账号已存在");
        }

        if (StringUtils.isNotEmpty(sysUser.getMobile()) &&
                null != sysUserRepository.findFirstByMobile(sysUser.getMobile())) {
            throw new RuntimeException("手机号码已存在");
        }

        sysUser.setSalt(RandomStringUtils.random(8, true, false));
        sysUser.setPassword(DigestUtils.md5DigestAsHex(String.format("%s%s", sysUser.getSalt(),
                sysUser.getPassword()).getBytes(StandardCharsets.UTF_8)));
        sysUser.setCreateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysUser.setUpdateBy(sysUser.getCreateBy());
        sysUser = sysUserRepository.save(sysUser);

        // 关联岗位
        if (CollectionUtils.isNotEmpty(sysUser.getPostIds())) {
            List<SysUserPost> sysUserPosts = new ArrayList<>();
            for (Long postId : sysUser.getPostIds()) {
                sysUserPosts.add(new SysUserPost().setPostId(postId).setUserId(sysUser.getId()));
            }
            sysUserPostRepository.saveAll(sysUserPosts);
        }

        // 关联角色
        if (CollectionUtils.isNotEmpty(sysUser.getRoleIds())) {
            List<SysUserRole> sysUserRoles = new ArrayList<>();
            for (Long roleId : sysUser.getRoleIds()) {
                sysUserRoles.add(new SysUserRole().setRoleId(roleId).setUserId(sysUser.getId()));
            }
            sysUserRoleRepository.saveAll(sysUserRoles);
        }

        return sysUser;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUser edit(SysUser sysUser) {
        SysUser newSysUser = sysUserRepository.findFirstById(sysUser.getId());
        if (null == newSysUser) {
            throw new RuntimeException("404 NotFound");
        }

        if (null != sysUserRepository.findFirstByUsernameAndIdNot(
                sysUser.getUsername(), newSysUser.getId())) {
            throw new RuntimeException("登录账号已存在");
        }

        if (StringUtils.isNotEmpty(sysUser.getMobile()) &&
                null != sysUserRepository.findFirstByMobileAndIdNot(sysUser.getMobile(), newSysUser.getId())) {
            throw new RuntimeException("手机号码已存在");
        }

        newSysUser.edit(sysUser);
        newSysUser.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        newSysUser = sysUserRepository.save(newSysUser);

        sysUserPostRepository.deleteByUserId(newSysUser.getId());
        sysUserPostRepository.flush();
        // 关联岗位
        if (CollectionUtils.isNotEmpty(sysUser.getPostIds())) {
            List<SysUserPost> sysUserPosts = new ArrayList<>();
            for (Long postId : sysUser.getPostIds()) {
                sysUserPosts.add(new SysUserPost().setPostId(postId).setUserId(sysUser.getId()));
            }
            sysUserPostRepository.saveAll(sysUserPosts);
        }

        sysUserRoleRepository.deleteByUserId(newSysUser.getId());
        sysUserRoleRepository.flush();
        // 关联角色
        if (CollectionUtils.isNotEmpty(sysUser.getRoleIds())) {
            List<SysUserRole> sysUserRoles = new ArrayList<>();
            for (Long roleId : sysUser.getRoleIds()) {
                sysUserRoles.add(new SysUserRole().setRoleId(roleId).setUserId(sysUser.getId()));
            }
            sysUserRoleRepository.saveAll(sysUserRoles);
        }

        return newSysUser;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(List<Long> ids) {
        sysUserRepository.deleteAllByIdInBatch(ids);
        sysUserRoleRepository.deleteByUserIdIn(ids);
        sysUserPostRepository.deleteByUserIdIn(ids);
    }

    @Override
    public void resetPass(ResetPassDTO resetPassDTO) {
        resetPassDTO.validate();

        SysUser sysUser = sysUserRepository.findFirstById(resetPassDTO.getId());
        if (null == sysUser) {
            throw new RuntimeException("404 NotFound");
        }

        String newPass = DigestUtils.md5DigestAsHex(String.format("%s%s", sysUser.getSalt(),
                resetPassDTO.getNewPass()).getBytes(StandardCharsets.UTF_8));
        sysUser.setPassword(newPass);
        sysUser.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysUserRepository.save(sysUser);
    }

    @Override
    public void modifyPass(ModifyPassDTO modifyPassDTO) {

        modifyPassDTO.validate();

        SysUser sysUser = sysUserRepository.findFirstById(LoginInfoAccessContext.getLoginInfo().getId());
        if (null == sysUser) {
            throw new RuntimeException("404 NotFound");
        }

        String currentPass = DigestUtils.md5DigestAsHex(String.format("%s%s",
                sysUser.getSalt(), modifyPassDTO.getCurrentPass()).getBytes(StandardCharsets.UTF_8));
        if (!sysUser.getPassword().equals(currentPass)) {
            throw new RuntimeException("当前密码不正确");
        }

        String newPass = DigestUtils.md5DigestAsHex(String.format("%s%s",
                sysUser.getSalt(), modifyPassDTO.getNewPass()).getBytes(StandardCharsets.UTF_8));
        sysUser.setPassword(newPass);
        sysUser.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysUserRepository.save(sysUser);
    }

    @Override
    public void updateAvatar(String avatar) {
        SysUser sysUser = sysUserRepository.findFirstById(LoginInfoAccessContext.getLoginInfo().getId());
        sysUser.setAvatar(avatar);
        sysUser.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysUserRepository.save(sysUser);
    }

    @Override
    public void modifyUser(ModifyUserDTO modifyUserDTO) {
        if (null != sysUserRepository.findFirstByMobileAndIdNot(modifyUserDTO.getMobile(),
                LoginInfoAccessContext.getLoginInfo().getId())) {
            throw new RuntimeException("手机号码已被占用");
        }

        SysUser sysUser = sysUserRepository.findFirstById(LoginInfoAccessContext.getLoginInfo().getId());
        BeanUtils.copyProperties(modifyUserDTO, sysUser);
        sysUser.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysUserRepository.save(sysUser);
    }
}
